/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.farmcode.core.parser.velocity;

import com.farmcode.api.parser.ParserEngineAPI;
import com.farmcode.api.parser.ParserException;
import com.farmcode.api.runtime.BaseResponse;
import com.farmcode.api.runtime.database.DatabaseEngineException;
import com.farmcode.api.runtime.database.DatabaseExplorerAPI;
import com.farmcode.api.runtime.FARMCodeContext;
import com.farmcode.api.runtime.Metadata;
import com.farmcode.api.runtime.Response;
import com.farmcode.api.runtime.TemplateCollection;
import com.farmcode.api.runtime.TemplateInfo;
import com.farmcode.api.runtime.metadata.ObjectInfo;
import com.farmcode.api.runtime.metadata.Procedure;
import com.farmcode.api.runtime.metadata.Table;
import com.farmcode.api.runtime.metadata.View;
import com.farmcode.api.runtime.regex.Match;
import com.farmcode.api.runtime.regex.MatchContext;
import com.farmcode.api.runtime.regex.MatchContextCollection;
import com.farmcode.core.parser.util.Function;
import com.farmcode.server.config.InitialContext;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;


import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Stateless;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.util.StringUtils;

/**
 *
 * @author oiprado
 */
@Local(ParserEngineAPI.class)
@Stateless(mappedName = "ejb/parcerEngine", name = "ejb/parcerEngine")
public class VelocityParserBean implements ParserEngineAPI {

    @EJB
    private DatabaseExplorerAPI databaseEngineAPI = null;
    private VelocityContext velocityContext = null;
    private FARMCodeContext context;
    private String homePath;
    private String templatesPath;
    private String outputPath;
    private String base;
    private Response<String> response;
//    private Properties baseConfig = new Properties();

    public VelocityParserBean() {
    }

    @Override
    public Response<String> parse(FARMCodeContext context) throws ParserException {
        response = new BaseResponse<String>();
        try {
            this.context = context;
            homePath = InitialContext.configure().getProperty("com.farmcode.server.home");
            templatesPath = InitialContext.configure().getProperty("com.farmcode.server.path.templates");
            outputPath = InitialContext.configure().getProperty("com.farmcode.server.path.output");
            TemplateCollection templateCollection = (TemplateCollection) context.get(FARMCodeContext.TEMPLATES);
            Metadata metadata = (Metadata) context.get(FARMCodeContext.METADATA);

            for (TemplateInfo frcTemplate : templateCollection) {
                if(frcTemplate.isUsed()){
                    Template template = initTemplate(context, frcTemplate);
                    response.getStackTrace().add("Template: " + template.getName());
                    for (List<ObjectInfo> objectInfos : metadata.getObjectInfos()) {
                        for (ObjectInfo objectInfo : objectInfos) {
                            response.getStackTrace().add("ObjectInfo: " + objectInfo.toString());
                            if(objectInfo.isUsed()){
                                useObjectInfo(objectInfo, template, frcTemplate);
                            }
                            response.getStackTrace().add("ObjectInfo used: " + objectInfo.isUsed());
                        }
                    }   
                }
            }
        } catch (Exception ex) {
            throw new ParserException(ex);
        }

        return response;
    }

    private void useObjectInfo(ObjectInfo objectInfo, Template template, TemplateInfo frcTemplate) throws DatabaseEngineException, IOException {
        if (objectInfo.isUsed()) {
            int size = 0;

            if (objectInfo instanceof Procedure) {
                response.addMessage("Object Type: Procedure");
                size = ((Procedure) objectInfo).getOutputChilds().size();
            } else if (objectInfo instanceof Table) {
                response.addMessage("Object Type: Table");
                size = ((Table) objectInfo).getAttributeChilds().size();
            } else if (objectInfo instanceof View) {
                response.addMessage("Object Type: View");
                size = ((View) objectInfo).getAttributeChilds().size();
            }
            if (size == 0) {
                context.put(FARMCodeContext.OBJECT_TYPE, objectInfo.getType().toLowerCase());
                objectInfo = (ObjectInfo) databaseEngineAPI.getColumn(context, objectInfo);
            }
            merge(context, template, frcTemplate, objectInfo);
        }
    }

    private Template initTemplate(HashMap context, TemplateInfo frcTemplate) throws IOException {
        base = getFileResource(buildPath(outputPath, (String)context.get(FARMCodeContext.PROJECT_NAME)));
        frcTemplate.put("HOME", homePath);
        
        velocityContext = new VelocityContext(context);
        velocityContext.put("fn", new Function(new Match(frcTemplate.getMatchContextCollection())));
        
        String templates = getFileResource(buildPath(templatesPath,(String) context.get(FARMCodeContext.TEMPLATE_PATH)));
        Properties properties = new Properties();
        properties.setProperty("file.resource.loader.path", templates);

        Velocity.init(properties);
        Template template = Velocity.getTemplate(frcTemplate.getName());

        return template;
    }

    private void merge(HashMap context, Template template, TemplateInfo frcTemplate, ObjectInfo dbo) throws IOException {
        String filepath = buildFilePath(frcTemplate.getDestiny(), dbo.getName(), context, frcTemplate);
        StringWriter stringWriter = new StringWriter();
        velocityContext.put("dbo", dbo);
        velocityContext.put("match", new Match(frcTemplate.getMatchContextCollection()));
        template.merge(velocityContext, stringWriter);
        saveFile(stringWriter.toString(), filepath, dbo.getClassName(), frcTemplate.getExtension());
    }

    private String buildFilePath(String destiny, String dbo, HashMap context, TemplateInfo templateInfo) {
        String dir = StringUtils.stringSubstitution(destiny, context).toString();
        MatchContextCollection contextContext = new MatchContextCollection();
        
        if(templateInfo.getMatchContextCollection() != null){
            if (templateInfo.getMatchContextCollection().size() > 0) {
                contextContext = templateInfo.getMatchContextCollection();
                Match match = new Match();
                match = new Match(contextContext);

                MatchContext matchContext = match.lookupContext(dbo);
                if (matchContext != null) {
                    if (matchContext.get("relocate") != null && matchContext.get("relocate").equals("Y")) {
                        if (matchContext.get("relocatePath") != null) {
                            dir = StringUtils.stringSubstitution(matchContext.get("relocatePath"), matchContext).toString();
                        }
                    }
                    for (String key : matchContext.keySet()) {
                        context.put(key, matchContext.get(key));
                    }
                }
            }
        }
        return base + dir;
    }
    
    private String buildPath(String... tokens){
        StringBuilder path = new StringBuilder();
        for (int i = 0; i < tokens.length; i++) {
            path.append(tokens[i]);
            path.append(File.separator);
        }
        return path.toString();
    }
    
    private String getFileResource(String token) throws IOException {
        StringBuilder path = new StringBuilder();
        path.append(homePath);
        path.append(File.separator);
        path.append(token);
        return path.toString();
    }
    
    private void saveFile(String contentFile, String dir, String fileName, String extension) throws IOException {
        File dirs = new File(dir);
        dirs.mkdirs();
        File file = new File(dir + fileName + "." + extension);
        file.createNewFile();
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"));
        out.write(contentFile);
        out.close();
    }
}
